package chan.project.controller;

import chan.project.common.*;
import chan.project.constant.LocationConstant;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import chan.project.annotation.AuthCheck;
import chan.project.constant.UserConstant;
import chan.project.exception.BusinessException;
import chan.project.exception.ThrowUtils;
import chan.project.model.dto.location.LocationAddRequest;
import chan.project.model.dto.location.LocationEditRequest;
import chan.project.model.dto.location.LocationQueryRequest;
import chan.project.model.dto.location.LocationUpdateRequest;
import chan.project.model.entity.Location;
import chan.project.model.entity.User;
import chan.project.model.vo.LocationVO;
import chan.project.service.LocationService;
import chan.project.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 地点接口
 *
 * @author <a href="https://github.com/kavansp">kavansp</a>
 * 
 */
@RestController
@RequestMapping("/location")
@Slf4j
public class LocationController {

    @Resource
    private LocationService locationService;

    @Resource
    private UserService userService;

    @Resource
    private RedisTemplate<String,Object> redisTemplate;


    // region 增删改查

    /**
     * 创建地点
     *
     * @param locationAddRequest
     * @param request
     * @return
     */
    @PostMapping("/add")
    public BaseResponse<Long> addLocation(@RequestBody LocationAddRequest locationAddRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(locationAddRequest == null, ErrorCode.PARAMS_ERROR);
        Location location = new Location();
        BeanUtils.copyProperties(locationAddRequest, location);
        // 数据校验
        locationService.validLocation(location, true);
        // 写入数据库
        boolean result = locationService.save(location);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 返回新写入的数据 id
        long newLocationId = location.getId();
        return ResultUtils.success(newLocationId);
    }

    /**
     * 删除地点
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteLocation(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User user = userService.getLoginUser(request);
        long id = deleteRequest.getId();
        // 判断是否存在
        Location oldLocation = locationService.getById(id);
        ThrowUtils.throwIf(oldLocation == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可删除
//        if (!oldLocation.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
//            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
//        }
        // 操作数据库
        boolean result = locationService.removeById(id);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    /**
     * 更新地点（仅管理员可用）
     *
     * @param locationUpdateRequest
     * @return
     */
    @PostMapping("/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateLocation(@RequestBody LocationUpdateRequest locationUpdateRequest) {
        if (locationUpdateRequest == null || locationUpdateRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Location location = new Location();
        BeanUtils.copyProperties(locationUpdateRequest, location);
        // 数据校验
        locationService.validLocation(location, false);
        // 判断是否存在
        long id = locationUpdateRequest.getId();
        Location oldLocation = locationService.getById(id);
        ThrowUtils.throwIf(oldLocation == null, ErrorCode.NOT_FOUND_ERROR);
        // 操作数据库
        boolean result = locationService.updateById(location);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    /**
     * 根据 id 获取地点（封装类）
     *
     * @param id
     * @return
     */
    @GetMapping("/get/vo")
    public BaseResponse<LocationVO> getLocationVOById(long id, HttpServletRequest request) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        // 查询数据库
        Location location = locationService.getById(id);
        ThrowUtils.throwIf(location == null, ErrorCode.NOT_FOUND_ERROR);
        // 获取封装类
        return ResultUtils.success(locationService.getLocationVO(location, request));
    }

    /**
     * 分页获取地点列表（仅管理员可用）
     *
     * @param locationQueryRequest
     * @return
     */
    @PostMapping("/list/page")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<Location>> listLocationByPage(@RequestBody LocationQueryRequest locationQueryRequest) {
        long current = locationQueryRequest.getCurrent();
        long size = locationQueryRequest.getPageSize();
        // 查询数据库
        Page<Location> locationPage = locationService.page(new Page<>(current, size),
                locationService.getQueryWrapper(locationQueryRequest));
        return ResultUtils.success(locationPage);
    }

    /**
     * 分页获取地点列表（封装类）
     *
     * @param locationQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/list/page/vo")
    public BaseResponse<Page<LocationVO>> listLocationVOByPage(@RequestBody LocationQueryRequest locationQueryRequest,
                                                               HttpServletRequest request) {
        long current = locationQueryRequest.getCurrent();
        long size = locationQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        // 查询数据库
        Page<Location> locationPage = locationService.page(new Page<>(current, size),
                locationService.getQueryWrapper(locationQueryRequest));
        // 获取封装类
        return ResultUtils.success(locationService.getLocationVOPage(locationPage, request));
    }

    /**
     * 分页获取当前登录用户创建的地点列表
     *
     * @param locationQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/my/list/page/vo")
    public BaseResponse<Page<LocationVO>> listMyLocationVOByPage(@RequestBody LocationQueryRequest locationQueryRequest,
                                                                 HttpServletRequest request) {
        ThrowUtils.throwIf(locationQueryRequest == null, ErrorCode.PARAMS_ERROR);
        // 补充查询条件，只查询当前登录用户的数据
        User loginUser = userService.getLoginUser(request);
//        locationQueryRequest.setUserId(loginUser.getId());
        long current = locationQueryRequest.getCurrent();
        long size = locationQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        // 查询数据库
        Page<Location> locationPage = locationService.page(new Page<>(current, size),
                locationService.getQueryWrapper(locationQueryRequest));
        // 获取封装类
        return ResultUtils.success(locationService.getLocationVOPage(locationPage, request));
    }

    /**
     * 编辑地点（给用户使用）
     *
     * @param locationEditRequest
     * @param request
     * @return
     */
    @PostMapping("/edit")
    public BaseResponse<Boolean> editLocation(@RequestBody LocationEditRequest locationEditRequest, HttpServletRequest request) {
        if (locationEditRequest == null || locationEditRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Location location = new Location();
        BeanUtils.copyProperties(locationEditRequest, location);
        // 数据校验
        locationService.validLocation(location, false);
        User loginUser = userService.getLoginUser(request);
        // 判断是否存在
        long id = locationEditRequest.getId();
        Location oldLocation = locationService.getById(id);
        ThrowUtils.throwIf(oldLocation == null, ErrorCode.NOT_FOUND_ERROR);
        // 操作数据库
        boolean result = locationService.updateById(location);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    // endregion

    /**
     * 分页查询排行榜
     * @param pageRequest
     * @return
     */
    @PostMapping("/rankings/list/page/vo")
    public BaseResponse<Page<LocationVO>> listLocationRankByPage(@RequestBody PageRequest pageRequest) {
        ThrowUtils.throwIf(pageRequest == null, ErrorCode.PARAMS_ERROR);
        int current = pageRequest.getCurrent();//当前页面
        int size = pageRequest.getPageSize();//页面大小
        // 限制爬虫
        ThrowUtils.throwIf(size > 50, ErrorCode.PARAMS_ERROR);
        // 查询缓存中的排行榜
        Page<LocationVO> locationVOPage = new Page<>(current, size);
        //将ZSet中的范围查询变为分页查询
        int start = size * (current-1);
        int end = size * current - 1;
        long total = redisTemplate.opsForZSet().size(LocationConstant.LOCATION_RANK);
        Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet().reverseRangeWithScores(LocationConstant.LOCATION_RANK, start, end);
        if(CollectionUtil.isEmpty(typedTuples)){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR,"排行榜不存在");
        }
        List<LocationVO> locationVOList = new ArrayList<>();
        // 遍历结果
        for (ZSetOperations.TypedTuple<Object> tuple : typedTuples) {
            String locationName = (String) tuple.getValue(); // 获取地点
            Double score = tuple.getScore(); // 获取分数
            LambdaQueryWrapper<Location> locationLambdaQueryWrapper = new LambdaQueryWrapper<>();
            locationLambdaQueryWrapper.eq(Location::getName,locationName);
            Location location = locationService.getOne(locationLambdaQueryWrapper);
            LocationVO locationVO = LocationVO.objToVo(location);
            locationVO.setScore(score);
            locationVOList.add(locationVO);
        }

        locationVOPage.setRecords(locationVOList);
        locationVOPage.setCurrent(current);
        locationVOPage.setSize(size);
        locationVOPage.setTotal(total);
        // 获取封装类
        return ResultUtils.success(locationVOPage);
    }
}
